Skip to content

17. 视图/模板引擎

📝 模块更新日志 新特性*

+ 新增 视图引擎支持完整的 `C#` 代码操作,如数据库操作 4\.9\.5\.10 ⏱️2024\.09\.19 [a571373](https://gitee.com/dotnetchina/Furion/commit/a57137300ae1334cf0ef141e4edd21894e801d35)
+ 新增 `TP.WrapperRectangle` 绘制矩形日志模板 4\.8\.8\.25 ⏱️2023\.06\.14 [60ffd76](https://gitee.com/dotnetchina/Furion/commit/60ffd76783633ac4fc7baaf845e14cb59518b795)
+ 新增 视图引擎支持无命名空间的强类型 4\.8\.4\.16 ⏱️2023\.01\.15 [\#I6ABN3](https://gitee.com/dotnetchina/Furion/issues/I6ABN3) [\#I6A7SI](https://gitee.com/dotnetchina/Furion/issues/I6A7SI) [076bb17](https://gitee.com/dotnetchina/Furion/commit/076bb1781ab1a31b5b6a42a56910909b3528d25e)
+ 新增 视图引擎支持匿名类型模型带集合类型属性 `@foreach` 遍历 4\.8\.4\.15 ⏱️2023\.01\.13 [\#I6A7SI](https://gitee.com/dotnetchina/Furion/issues/I6A7SI)
  • 问题修复

    • 修复 视图引擎 ViewEngine 存在内存未释放(内存溢出风险)情况 4.9.4.4 ⏱️2024.07.10 c9e203a
    • 修复 模板引擎不支持将粘土对象或 DynamicObject 派生类类型设置为模板数据 4.9.2.36 ⏱️2024.05.05 07ee172
    • 修复 模板引擎高并发读取缓存模板出现线程占用问题 4.8.8.43 ⏱️2023.09.14 #I80ZKB
    • 修复 TP.Wrapper 静态类不能准确识别多行内容问题 4.8.7.40 ⏱️2023.04.10 #I6UAC8
    • 修复 视图引擎模型为匿名泛型集合类型时出现类型转换异常 4.8.7.38 ⏱️2023.04.07 !773
    • 修复 视图引擎不支持强制转换的 (object)model 类型 4.8.7.16 ⏱️2023.03.19 #I6O3BD
    • 其他更改

    • 调整 模板引擎编译性能,提升模板缓存解析速度 4.9.1.16 ⏱️2023.12.27 d7ea423

    • 调整 视图引擎默认程序集,追加 System.Collections 程序集 4.8.7.16 ⏱️2023.03.18 #I6O3BD

17.1 关于视图引擎

视图引擎负责根据视图模板创建 HTML。视图通常是 HTML 和编程语言的某种混合。支持变量定义、方法调用及逻辑编写。

Furion 框架中,底层集成了微软提供的 Razor 视图引擎组件并提供更加灵活方便的语法糖。

17.2 视图引擎作用

  • 支持 ASP.NET Core 完整的 Razor 语法
  • 根据不同的数据编译模板产生不同的输出
  • 实现强大的插件化机制
  • 实现全站页面静态化
  • 可以用作邮件模板、短信模板、优惠券信息模板等

17.3 基础使用

17.3.1 注册服务

使用之前需在 Startup.cs 中注册 视图引擎服务

public void ConfigureServices(IServiceCollection services)  
{  
    services.AddViewEngine();  
}  

17.3.2 使用方式

  • 构造函数注入 IViewEngine
using Furion.DynamicApiController;  
using Furion.ViewEngine;  

namespace Furion.Application  
{  
    public class ViewEngineService : IDynamicApiController  
    {  
        private readonly IViewEngine _viewEngine;  
        public ViewEngineService(IViewEngine viewEngine)  
        {  
            _viewEngine = viewEngine;  
            var result = _viewEngine.RunCompile("Hello @Model.Name", new { Name = "Furion" });  
        }  
    }  
}  

  • 字符串方式
var result = "Hello @Model.Name".RunCompile(new { Name = "Furion" });  

17.3.3 弱类型模板

var result = _viewEngine.RunCompile("Hello @Model.Name", new { Name = "Furion" });  

结果:

Hello Furion  

支持异步 RunCompileAsync

17.3.4 强类型模板

  • 类型定义
namespace YourProject;  // Furion 4.8.4.16+ 支持无命名空间写法  

public class TestModel  
{  
    public string Name { get; set; }  
    public int[] Items { get; set; }  
}  

  • 使用强类型
var result = _viewEngine.RunCompile(@"  
Hello @Model.Name  
@foreach(var item in Model.Items)  
{  
    <p>@item</p>  
}  
", new TestModel    // Furion 4.8.4.16+ 支持匿名类型  
{  
    Name = "Furion",  
    Items = new[] { 3, 1, 2 }  
});  

结果:

Hello Furion  
<p>3</p>  
<p>1</p>  
<p>2</p>  

支持异步 RunCompileAsync

17.3.5 高性能模板缓存 🥇

由于模板编译需要消耗大量的性能,所以建议使用带 FromCached 结尾的 RunCompileFromCached 替代。调用该方法后会自动将模板编译成 .dll 以便下次使用。减少第二次之后使用模板的性能损耗。

如,强类型模板:

var result = _viewEngine.RunCompileFromCached(@"  
Hello @Model.Name  
@foreach(var item in Model.Items)  
{  
    <p>@item</p>  
}  
", new TestModel    // Furion 4.8.4.16+ 支持匿名类型  
{  
    Name = "Furion",  
    Items = new[] { 3, 1, 2 }  
});  

结果:

Hello Furion  
<p>3</p>  
<p>1</p>  
<p>2</p>  

调用 RunCompileFromCached 方法之后将会使用 MD5 加密模板并生成 MD5字符串的 .dll 存放在网站根目录下的 templates 目录中。只要模板内容不变,数据发生改变也不会重新编译模板。这样大大的提高了首次之后的性能。

如,传入新的数据:

var result = _viewEngine.RunCompileFromCached(@"  
Hello @Model.Name  
@foreach(var item in Model.Items)  
{  
    <p>@item</p>  
}  
", new TestModel    // Furion 4.8.4.16+ 支持匿名类型  
{  
    Name = "Furion",  
    Items = new[] { 5,6,7,8 }  
});  

结果:

Hello Furion  
<p>5</p>  
<p>6</p>  
<p>7</p>  
<p>8</p>  

模板不再重新编译,只是重新替换数据。

17.4 高级用法

高级用法支持将特定程序集、特定命名空间、特定类型引入到模板中使用。

17.4.1 添加程序集

比如这里添加 System.IO 程序集:

var result = _viewEngine.RunCompileFromCached(@"<div>@System.IO.Path.Combine(""Furion"", ""ViewEngine"")</div>", builderAction: builder =>  
            {  
                builder.AddAssemblyReferenceByName("System.IO");  
            });  

结果:

<div>Furion\\ViewEngine</div>  

另外,Furion 提供多种方式加载程序集:

builder.AddAssemblyReferenceByName("System.Security"); // 通过名称  
builder.AddAssemblyReference(typeof(System.IO.File)); // 通过类型  
builder.AddAssemblyReference(Assembly.Load("source")); // 通过元数据引用  

17.4.2 添加命名空间

var result = _viewEngine.RunCompileFromCached(@"<div>@Path.Combine(""Furion"", ""ViewEngine"")</div>", builderAction: builder =>  
            {  
                builder.AddUsing("System.IO");  
                builder.AddAssemblyReferenceByName("System.IO");  
            });  

结果:

<div>Furion\\ViewEngine</div>  

也支持加入多个 using

builder.AddUsing("System.IO");  
builder.AddUsing("Furion");  

17.4.3 定义模板方法

var result = _viewEngine.RunCompileFromCached(@"  
<area>  
    @{ RecursionTest(3); }  
</area>  

@{  
  void RecursionTest(int level)  
  {  
    if (level <= 0)  
    {  
        return;  
    }  

    <div>LEVEL: @level</div>  
    @{ RecursionTest(level - 1); }  
  }  
}  
");  

结果:

<area>  
<div>LEVEL: 3</div>  
<div>LEVEL: 2</div>  
<div>LEVEL: 1</div>  
</area>  

17.4.4 调用类方法

定义 CustomModel 类并继承 ViewEngineModel 基类

public class CustomModel : ViewEngineModel  
{  
    public int A { get; set; }  
    public string B { get; set; }  

    public string Decorator(object value)  
    {  
        return "-=" + value + "=-";  
    }  
}  

在模板中调用 Decorator(value) 方法:

var content = @"Hello @A, @B, @Decorator(123)";  

var template = _viewEngine.Compile<CustomModel>(content);  

var result = template.Run(instance =>  
{  
    instance.A = 10;  
    instance.B = "Alex";  
});  

结果:

Hello 10, Alex, -=123=-  

17.5 特殊字符处理

如有特殊符号如:<<>& 等符号,可通过 @("<")@('<') 进行原样输出,如:

var str = "bool value = 1 @('<') 2";  // 源字符串为:bool value = 1 < 2;  

17.6 字符串模板替换引擎

Furion 除了内置视图引擎之外,还支持以下几种模板替换,如:

// 提供数据模板方式  
var str = "我叫{name}".Render(new Dictionary{ {"name", "Furion"} });  
var str = "我叫{Name}".Render(new { Name = "Furion" });  
var str = "我叫{Detail.Name}".Render(new { Detail = new { Name = "Furoin" } });  

// 从配置读取方式  
var str = "我叫#(Furion:Address)".Render();  

{  
  "Furion": {  
    "Address": "https://furion.net"  
  }  
}  

17.7 格式化规范模板

Furion v3.5.3+ 新增了 TP.Wrapper(...) 规范模板,使用如下:

// 生成模板字符串  
var template = TP.Wrapper("Furion 框架", "让 .NET 开发更简单,更通用,更流行。",  
    "##作者## 百小僧",  
    "##当前版本## v3.5.3",  
    "##文档地址## https://furion.net",  
    "##Copyright## 百小僧及百签科技(广东)有限公司");  

Console.WriteLine(template);  

日志打印模板如下:

┏━━━━━━━━━━━  Furion 框架 ━━━━━━━━━━━  
┣ 让 .NET 开发更简单,更通用,更流行。  
┣  
┣ 作者:        百小僧  
┣ 当前版本:     v3.5.3  
┣ 文档地址:     https://furion.net  
┣ Copyright:   百小僧及百签科技(广东)有限公司  
┗━━━━━━━━━━━  Furion 框架 ━━━━━━━━━━━  

关于属性生成如果列表项以 ##属性名## 开头,自动生成 属性名: 作为行首且自动等宽对齐。

Furion 3.9.1 之前版本使用 [属性名] 开头。

17.8 生成矩形日志模板

版本说明以下内容仅限 Furion 4.8.8.25 + 版本使用。

var template = TP.WrapperRectangle(new[] {  
  "百小僧",  
  "让 .NET 开发更简单,更通用,更流行。",  
  "一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。"  
});  

Console.WriteLine(template);  

日志打印模板如下:

+-----------------------------------------------------------------------------+  
|                                   百小僧                                    |  
|                    让 .NET 开发更简单,更通用,更流行。                      |  
|         一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。           |  
+-----------------------------------------------------------------------------+  

还可以配置左对齐,居中对齐,右对齐:

// 左对齐  
var template = TP.WrapperRectangle(new[] {  
  "百小僧",  
  "让 .NET 开发更简单,更通用,更流行。",  
  "一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。"  
}, -1); // -1 表示左对齐  

// 居中对齐  
var template = TP.WrapperRectangle(new[] {  
  "百小僧",  
  "让 .NET 开发更简单,更通用,更流行。",  
  "一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。"  
}, 0); // 0 表示居中对齐  

// 右对齐  
var template = TP.WrapperRectangle(new[] {  
  "百小僧",  
  "让 .NET 开发更简单,更通用,更流行。",  
  "一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。"  
}, 1); // 1 表示右对齐  

输出如下:

+-----------------------------------------------------------------------------+  
|  百小僧                                                                     |  
|  让 .NET 开发更简单,更通用,更流行。                                        |  
|  一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。                  |  
+-----------------------------------------------------------------------------+  

+-----------------------------------------------------------------------------+  
|                                   百小僧                                    |  
|                    让 .NET 开发更简单,更通用,更流行。                      |  
|         一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。           |  
+-----------------------------------------------------------------------------+  

+-----------------------------------------------------------------------------+  
|                                                                     百小僧  |  
|                                        让 .NET 开发更简单,更通用,更流行。  |  
|                  一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。  |  
+-----------------------------------------------------------------------------+  

另外还可以配置矩形最长字符串的追加长度。

// 右对齐  
var template = TP.WrapperRectangle(new[] {  
  "百小僧",  
  "让 .NET 开发更简单,更通用,更流行。",  
  "一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。"  
}, 1, 20); // 20 表示最长字符串长度 + 20  

17.9 反馈与建议

与我们交流给 Furion 提 Issue